package cn.nr19.jian.factory

import cn.nr19.jian.J2Utils
import cn.nr19.jian.Jian
import cn.nr19.jian.exception.ParserException
import cn.nr19.jian.exception.TokenException
import cn.nr19.jian.token.ARRNode
import cn.nr19.jian.token.BooleanNode
import cn.nr19.jian.token.BreakNode
import cn.nr19.jian.token.ContinueNode
import cn.nr19.jian.token.EONNode
import cn.nr19.jian.token.EONJNode
import cn.nr19.jian.token.ForNode
import cn.nr19.jian.token.FuzhiNode
import cn.nr19.jian.token.GFunNode
import cn.nr19.jian.token.NVarNode
import cn.nr19.jian.token.Node
import cn.nr19.jian.token.IFNode
import cn.nr19.jian.token.IFX
import cn.nr19.jian.token.IFXIS
import cn.nr19.jian.token.IFXOR
import cn.nr19.jian.token.J2Node
import cn.nr19.jian.token.NEventNode
import cn.nr19.jian.token.NFunNode
import cn.nr19.jian.token.NumNode
import cn.nr19.jian.token.Obj1Node
import cn.nr19.jian.token.Obj2Node
import cn.nr19.jian.token.PN
import cn.nr19.jian.token.ParNode
import cn.nr19.jian.token.ParTypeNode
import cn.nr19.jian.token.ReturnNode
import cn.nr19.jian.token.StrNode
import cn.nr19.jian.token.SuanNode
import cn.nr19.jian.token.VarNode
import cn.nr19.jian.token.VARSELF
import cn.nr19.jian.token.WhenNode
import cn.nr19.jian.token.ExNode
import cn.nr19.jian.token.E3Node
import cn.nr19.jian.token.ENode
import cn.nr19.jian.token.JsNode
import cn.nr19.jian.token.LayoutNode
import cn.nr19.jian.token.EToken
import cn.nr19.jian.token.IMPORT
import cn.nr19.jian.token.InNode
import cn.nr19.jian.token.LAYOUT
import cn.nr19.jian.token.NotesNode
import cn.nr19.jian.token.Token
import java.lang.IndexOutOfBoundsException


/**
 * 词(Token)解决后，解析节点咯
 */
class NodeFactory : NodeFactory2() {

    companion object{
        fun start(tokens : List<Token>) : List<Node> {
            return NodeFactory().start(tokens)
        }

        /**
         * @param pn 保留pn
         */
        fun start(code : String,pn : Boolean = true) :J2Node {
            J2Node().let {j2->
                if (pn) j2.nodes.addAll(start(TokenFactory().start(code)))
                else start(TokenFactory().start(code)).forEach {
                    if (it.nodeType() != ENode.pn) j2.nodes.add(it)
                }
                return j2
            }
        }

        // 解析编辑器的布局
        fun startLayout(code : String,nodeStartPositon : Int = 0) : LAYOUT{
            return NodeFactory().startLayout(code,nodeStartPositon)
        }
    }

    private fun start(tokens: List<Token>) : List<Node>{
        this.tokens.addAll(tokens)

        J2Node().let {
            var n = nextLine(it)
            while (n!=null){
                it.nodes.add(n)
                n = nextLine(it)
            }

            // 释放
            this.tokens.clear()

            // 回调节点
            return it.nodes
        }
    }


    /**
     * @param nodeStartPositon 节点开始位置
     */
    fun startLayout(code : String,nodeStartPositon : Int) : LAYOUT{
        if (code.isEmpty()) throw TokenException.inputError("请添加布局内容",nodeStartPositon)
        try {
            this.tokens.addAll(TokenFactory().start(code.replace("\n","\n")))

            // 第一行
            val one = pLayoutLine(nextToken())
            // if (one.level > 0) throw TokenException.inputError("布局语法有问题，首视图勿加大于号 ，大于号属于子视图。",0)

            var cur = one
            var nx = readNextToken()
            while (nx != null){
                pos++
                pLayoutLine(nx).let {
                    val parent =
                        // 子级
                        if (it.level > cur.level) cur

                        // 同级或父级
                        else{
                            var p = cur.parent

                            // 只要是大于就可
                            while (p != null && p.level >= it.level)
                                p = p.parent
                            p
                        }

                    it.parent = parent
                    parent?.child?.add(it)
                    // 转换当前视图位置
                    cur = it
                }
                nx = readNextToken()
            }

            return one
        }catch (e : TokenException){
            e.position += nodeStartPositon
            throw e
        }




    }

    private fun pLayoutLine(token: Token) : LAYOUT{
        val one = LAYOUT()

        // 大于好 深度
        if (token.type == EToken.dayu){
            one.level++
            var nx2 = readNextToken()
            while (nx2?.type == EToken.dayu) {
                pos++
                one.level++
                nx2 = readNextToken()
            }

            // key
            one.key = nextToken(true,EToken.KEY).text
        }
        else if (token.type == EToken.KEY){
            // key
            one.key = token.text
        }
        else{
            throw TokenException.inputError("布局语法有问题，请检查。【${token.type}-${token.text}】",token)
        }


        // 跟着小数点/属于参数
        var nx = readNextToken()
        while (nx?.type == EToken.xiaoshudian){
            pos++
            val parName = nextToken(true,EToken.KEY).text

            // name(zhi)
            nextToken(true,EToken.zkh)
            val zhi = zhi(nextToken())
            nextToken(true,EToken.ykh)



            one.pars.put(parName,zhi)
            nx = readNextToken()
        }

        return one
    }

    private fun prev(j2stat : J2Node) : Node {
        try {
            j2stat.nodes[ j2stat.nodes.size - 1].let {
                j2stat.nodes.removeAt(j2stat.nodes.size - 1)
                return it
            }
        }catch (e : IndexOutOfBoundsException){
            throw NullException()
        }
    }
    private fun next(j2stat : J2Node) : Node {
        return nextLine(j2stat) ?: throw NullException("代码不完整")
    }
    private fun nextLine(j2stat : J2Node) : Node?{
        if (tokens.size > pos)
            tokens.get(pos).let { token->
                // 首个 读取成功
                pos++
                return nextLine(token,j2stat)
            }
        else
            return null
    }
    private fun nextLine(token : Token,j2stat : J2Node): Node{

        // 首个 读取成功
        // 基础: nfun | nvar | event | nfor | nwhen | nif | import | in | notes |
        when(token.type){

            // nfun 功能
            EToken.FUN -> return pFun(token)

            // nvar 创建变量
            EToken.VAR ->{
                return NVarNode(token).let { nvar->
                    // 第一个值为 KEY ，变量名称
                    nvar.name = nextToken(true,EToken.KEY,"应输入变量名称").text

                    // 冒号 记录类型 可不输入
                    readNextToken(true).let {
                        if (it?.type == EToken.maohao){
                            pos++ // 冒号
                            nvar.type = parType()
                        }
                    }

                    // 等于号 后面取值
                    readNextToken(true).let {
                        if (it?.type== EToken.dengyu){
                            pos++
                            nvar.value = zhi(j2stat)
                        }
                    }

                    nvar
                }
            }

            // event 事件  xx.xx():partype{}
            EToken.EVENT -> {
                return NEventNode().let { nevent->

                    nevent.setStartToken(token)

                    // 第一个值为 KEY ，功能名称
                    nevent.name = nextToken(true,EToken.KEY,"应输入事件名称").text

                    // 后面跟着小数点  xx.xx  ， 不支持  xx[0].xx  , xx().xx 进支持 var.name

                    if (readNextToken()?.type == EToken.xiaoshudian){
                        nevent.target = VarNode(nevent.name,token.pos)
                        var nx = nextToken() // 前面的 readNextToken() 只读不取
                        while (nx.type == EToken.xiaoshudian){
                            nextToken(true,EToken.KEY).let { k ->
                                readNextToken().let {
                                    nx = nextToken()
                                    // 若后面还跟着有小数点，那这个属于目标
                                    if (it?.type==EToken.xiaoshudian) {
                                        nevent.target = Obj2Node(nevent.target!!,VarNode(k))
                                    }
                                    // 不然就结束
                                    else{
                                        pos--  // nx 不符合 丢回去
                                        nevent.name = k.text
                                    }
                                }
                            }
                        }
                    }
                    // 参数
                    nevent.pars.addAll(gPars())


                    // 冒号 记录类型 可不输入
                    readNextToken(true).let {
                        if (it?.type == EToken.maohao){
                            pos++ // 冒号
                            nevent.type = parType()
                        }
                    }

                    // 命令
                    nevent.j2 = pStat(nextToken())

                    nevent
                }
            }

            // nfor 循环
            EToken.FOR -> return pFor(j2stat)

            // nwhen 判断
            EToken.WHEN -> return pWhen()

            // nif 如果
            EToken.IF -> return pIf(j2stat)

            // 注释、空行 跳过
            EToken.zhushi -> return NotesNode(token.text)
            EToken.PN -> return PN(token.text)

            // 若后面跟着不是换行，则后面输出同行内容
            EToken.RETURN -> {
                // 必须同一行
                readNextToken(false).let {
                    // 非换行就读节点
                    when(it?.type){
                        // ke
                        EToken.KEY,EToken.Str,EToken.INT,EToken.TRUE,EToken.FALSE,EToken.js,EToken.j2,EToken.DOUBLE
                            -> {
                                pos++
                              return ReturnNode(nextLine(it,j2stat))
                            }
                        else-> return ReturnNode()
                    }

                }


            }

            // 跳出循环 跳过循环
            EToken.BREAK -> return BreakNode()
            EToken.CONTINUE -> return ContinueNode()

            // obj + obj,  obj - obj 运算规则
            EToken.jia,EToken.jian -> {
                return SuanNode().apply {
                    left = prev(j2stat)
                    ysf = token.text
                    right = next(j2stat)
                }
            }

            // prev = next
            EToken.dengyu,EToken.ysf2 -> {
                return FuzhiNode(prev(j2stat),token.text,next(j2stat))
            }

            // 继承
            EToken.IN -> return InNode(token,lei(nextToken(),nullJ2))

            //
            EToken.IMPORT ->{

                //  lx
                val lx = lei(nextToken(),nullJ2)

                // 名称
                var name = ""

                //  跟着
                readNextToken(true)?.let {
                    if (it.type == EToken.KEY && it.text == "作") {
                        pos++
                        nextToken().let {
                            if (it.type == EToken.KEY) name = it.text
                            else throw TokenException.inputError("语法错误",token)
                        }
                    }
                }

                if (name .isEmpty()) name = Jian.def().取文本右边2(lx,".")

                return IMPORT(token,lx,name)
            }

            // 后面的 可能性都大
            else -> {
                //J2Utils.log("类型",token.type,token.text)
                return zhi(token)
                // throw TokenException.inputError("zhi|无法正确匹配 : ${token.type}（${token.text}）",token)
            }
        }
    }




    // *************************************************
    // *********** 内置运算符 **************************

    private fun pFun(token: Token) : NFunNode {
        // fun name :partype (pars){j2}
        return NFunNode(token).let { nfun->

            // 第一个值为 KEY ，功能名称
            nfun.name = nextToken(true,EToken.KEY,"应输入功能名称").text

            // 冒号 记录类型 可不输入
            readNextToken(true).let {
                if (it?.type == EToken.maohao){
                    pos++ // 冒号
                    nfun.type = parType()
                }
            }

            // 参数
            nfun.pars.addAll(gPars())

            // 有冒号，有类型
            nextToken().let { nx->
                if (nx.type == EToken.maohao){
                    nfun.type = parType()
                    nfun.j2 = pStat(nextToken())
                }else{
                    // 执行
                    nfun.j2 = pStat(nx)
                }


            }

            nfun
        }

    }

    /**
     * 取如果 Node
     */
    private fun pIf(j2stat: J2Node) : IFNode {
        val pos2 = pos
        val f = IFNode()

        // 首个条件
        nextToken().let {
            f.setStartToken(it)
            f.ifxs.add(ifx(it,j2stat))
        }


        // { 命令开始
        nextToken(true,EToken.start)

        J2Node().let { j2->
            // 必须必须必须 先加入。否则后面循序跟不上来。 也不要在 pIfStat 中加入，该函数调用位置不仅仅只是pif
            f.stats.add(j2)

            pIfStat(f, nextToken(),j2)
        }

        return f
    }

    /**
     * 解析 stat 返回空则 执行完毕，闭包了
     */
    private fun pIfStat(f : IFNode, startToken: Token, stat : J2Node){
        // if(xx){ stat...    else if(xx){ stat...   else stat...    确保 stat 前面的已经取了， startToken 必须属于 stat 的值

        var token = startToken


        while (true){

            when(token.type){
                // } else , } else if  ，或者闭包
                EToken.end -> {
                    readNextToken().let {
                        // 为空 闭包 结束
                        if (it == null) return

                        when(it.type){
                            EToken.ELSE,EToken.ElseIf ->{
                                pos++

                                // 确保取掉 { 则

                                // 这里不打包stat
                                pIfStat(f,it,stat)
                            }
                            // 其它 闭包结束
                            else ->  return

                        }
                    }
                }

                EToken.ELSE -> {
                     nextToken().let {

                         // 接着是 if ， 转换 elseif 方式读取
                         if (it.type == EToken.IF){
                             it.type = EToken.ElseIf
                             pIfStat(f,it,stat)
                             return
                         }

                         J2Node().let { j2->
                             // 必须必须必须 先加入。否则后面循序跟不上来
                             f.stats.add(j2)

                             // else { stat ， pifStat 需确保去掉 {
                             pIfStat(f,if (it.type == EToken.start) nextToken() else it,j2)

                         }
                    }
                    return
                }

                EToken.ElseIf -> {
                    f.ifxs.add(ifx(nextToken(),stat))
                    nextToken().let {
                        (if (it.type == EToken.start) nextToken() else it).let { nxToken->
                            J2Node().let { j2->
                                f.stats.add(j2)
                                pIfStat(f, nxToken, j2)
                            }

                        }
                    }
                    return
                }

                else -> {
                    stat.nodes.add(nextLine(token,stat)?:throw NullException())
                    token = nextToken()
                }
            }
        }
    }

    /**
     * 取判断 (xx){a->{};b->{}}
     */
    private fun pWhen() : WhenNode {

        // when (xx) {
        //    a : {}          // 可以填括号
        //    b :             // 也可以不填
        // }
        val w = WhenNode()

        // def
        nextToken().let {
            w.setStartToken(it)
            w.ifxs.add(ifx(it,nullJ2))
        }


        // { 命令开始
        nextToken(true,EToken.start)

        // 取第一个节点
        nextToken().let {
            // 空判断 无闭包
            if (it.type == EToken.end) return w

            // 第一个就否则，玩呢
            else if (it.type == EToken.ELSE) throw NullException("判断 无节点就${it.text}？")

            nextLine(it,nullJ2).let {

                // 需有分隔符
                nextToken(true,EToken.maohao,"找不到判断节点分隔符 -> ")

                w.ifxs.add(it)

                nextToken().let {
                    val startSymbold = it.type == EToken.start
                    J2Node().let { j2->
                        w.stats.add(j2)
                        pWhenNode(w,if (startSymbold) nextToken() else it,j2,startSymbold,false)
                    }
                }
            }
        }


        return w
    }

    /**
     *
     * 取判断的 stat ， :  { 后面开始读   注意哦  这个带 { 大括号,
     * @param startToken 需 大括号(start) 后面的 token
     * @param j2  节点 stat
     * @param hasNodeStartSymbol 含节点闭包开始符。
     * @param isElse 这个 stat 是否为否则的指令，若是结束时就应当为闭包了
     */
    private fun pWhenNode(w : WhenNode, startToken: Token, j2 : J2Node, hasNodeStartSymbol : Boolean, isElse : Boolean){

        var token = startToken


        while (true){
            when(token.type){

                //  } node : ， 新的 node 或闭包
                EToken.end -> {

                    // 没有闭包符，属判断闭包。
                    if (!hasNodeStartSymbol) return

                    // 属最后一个节点，闭包否则 并闭包判断
                    if (isElse) {
                        nextToken(true,EToken.end)
                        return
                    }


                    // 剩下有一定概率存在其他闭包。需要读下一节点并确保下一节点后跟着的token.maohao
                    readNextToken()?.let {

                        // 闭包。
                        if (it.type == EToken.end){
                            pos++
                            return
                        }

                        // } else
                        if (it.type == EToken.ELSE) {
                            pos++

                            // 重复代码
                            pWhenNode(w,it,j2,false,false)
                            return
                        }

                        // 记录当前位置，取到下一个节点就回调
                        val curPos = pos

                        try {
                            nextLine(j2)?.let { nxLine->

                                // node :
                                if (readNextToken()?.type == EToken.maohao) {

                                    // 节点成立 记录下一节点
                                    w.ifxs.add(nxLine)

                                    pos++ // 取下节点指令分解符

                                    // 取下节点 stat 并闭包当前节点
                                    nextToken().let {
                                        J2Node().let { j2->
                                            w.stats.add(j2)
                                            val startSymbol = it.type == EToken.start
                                            pWhenNode(w,(if (startSymbol) nextToken() else it),j2,startSymbol,false)

                                            // 结束当前节点闭包
                                            return
                                        }
                                    }
                                }
                            }
                        }
                        catch (e : Exception){
                            // 无视错误
                        }

                        J2Utils.log("nx type",it.type)
                        // 执行到这里 也证明取下一节点失败。 闭包判断。
                        pos = curPos

                    }

                    // 这是闭包符
                    return
                }

                // else 最后一个 when
                EToken.ELSE -> {

                    // else :  // 也需要冒号
                    nextToken(true,EToken.maohao,"找不到判断节点符分界符 -> ")

                    // 取否则节点并闭包当前节点
                    nextToken().let {
                        J2Node().let { j2->
                            val nodeStartSymbol = it.type == EToken.start
                            w.stats.add(j2)
                            pWhenNode(w,(if (nodeStartSymbol) nextToken() else it),j2,nodeStartSymbol,true)
                            return
                        }
                    }
                }

                // 有一个节点没有被接住
                EToken.maohao -> {

                    // 上一个 node 应属于新的节点
                    if (j2.nodes.size == 0) throw ParserException.token("这个冒号是干啥的？",token)

                    // 成立下一个节点并删除上个节点误入值
                    w.ifxs.add(j2.nodes[j2.nodes.size - 1])
                    j2.nodes.removeAt(j2.nodes.size - 1)

                    // 读下个节点并闭包返回
                    nextToken().let {
                        J2Node().let { j2->
                            w.stats.add(j2)
                            val startSymbol = it.type == EToken.start
                            pWhenNode(w,(if (startSymbol) nextToken() else it),j2,startSymbol,false)
                            return
                        }
                    }


                }

                else -> {
                    j2.nodes.add(nextLine(token,j2)?:throw NullException())
                    token = nextToken()
                }

            }
        }

    }

    /**
     * 循环  (ifx) {}
     */
    private fun pFor(j2stat: J2Node) : ForNode {
        // for ( suan .. suan) {key: stat }
        // for ( ifx ) { key:stat }

        lateinit var f : ForNode

        // for (ifx) 是可以不用括号的
        nextToken().let {
            if (it.type == EToken.zkh){

                // for ( ifx
                f = ForNode(ifx(nextToken(),j2stat))


                // 接着为 符号  .. .<  就匹配第一条件   for ( left .. right
                readNextToken().let {
                    if (it?.type == EToken.FOR1 || it?.type == EToken.FOR2){
                        pos++
                        f.dao = it.type == EToken.FOR2
                        f.right = ifx(nextToken(),j2stat)
                    }
                }

                // 闭包括号  for ( left .. right )
                nextToken(true,EToken.ykh)
            }

            // 无括号  for xx 则  stat 完毕
            else{
                f = ForNode(ifx(nextToken(),j2stat))
            }
        }

        // ifx  条件成立则返回


        f.stat = pStat2(nextToken())

        return f
    }

    /**
     * 取stat 起包符和闭包符在这里读  { stat }
     */
    private fun pStat(statToken: Token) : J2Node {
        if (statToken.type != EToken.start) throw NullException("gstat|应当输入 指令闭包开始符 {  ?")

        val j2 = J2Node()


        // 先统一取出来
        var token = nextToken()
        while (token.type != EToken.end){
            j2.nodes.add(nextLine(token,j2))
            token = nextToken()
        }

        return j2
    }

    /**
     * 取stat 起包符和闭包符在这里读  {par0,par1-> stat }
     */
    private fun pStat2(statToken: Token) : J2Node {

        if (statToken.type != EToken.start) throw NullException("gstat2|应当输入 { } 指令闭包符 ，而你输入 ${statToken.text}")

        val j2 = J2Node()

        val poss = pos

        // 若第一个为 KEY ，就判断 gVarName
        readNextToken()?.let {

            //
            pos++

            var nx = it
            while (nx.type == EToken.KEY){
                j2.vars.add(nx.text)

                //还有下一个

                nextToken().let {
                    if (it.type == EToken.douhao) {
                        nx = nextToken()
                    }else{
                        nx = it
                    }
                }
            }

            // 闭包 冒号 ->  。  否则本身不具备参数，位置回到从最初
            if (nx.type != EToken.maohao) {
                j2.vars.clear()
                pos = poss
            }

        }

        var token = nextToken()
        while (token.type != EToken.end){
            j2.nodes.add(nextLine(token,j2)?:throw ParserException.node("指令未正常闭包结束",j2))
            token = nextToken()
        }



        return j2
    }


    // *************************************************
    // *********** 基础对象 *****************************
    // obj[suan] ，obj.obj，var++, gfun , arr , num , str , boolean , var , ZKH suan YKH

    // xx.xx.xx  只是类，必须读类时使用这个函数。目前用到就引用、继承
    protected fun lei(token: Token,j2stat: J2Node) : String{
        val strs = StringBuilder(token.text)
        var nx = readNextToken()
        while (nx?.type == EToken.xiaoshudian){
            pos++
            readNextToken().let {
                // 无法组成 xx.xx ，报错
                if (it?.type != EToken.KEY) {
                    pos--
                    throw TokenException.langError(token)
                }

                pos++
                strs.append(".").append(it.text)

                nx = readNextToken()
            }
        }
        return strs.toString()
    }

    //
    protected fun obj(token : Token,j2stat : J2Node) : Node {
        // 第一个词
        val one : Node? = when(token.type){
            // 逻辑
            EToken.FALSE -> obj3(BooleanNode(false),j2stat)
            EToken.TRUE -> obj3(BooleanNode(true),j2stat)

            // 数字  num
            EToken.INT -> obj3(NumNode(token.text.toInt()),j2stat)
            EToken.DOUBLE -> obj3(NumNode(token.text.toDouble()),j2stat)

            // 文本 str
            EToken.Str -> obj3(StrNode(token.text),j2stat)

            // 加减 1 数字，2    gobj5,gobj7,num
            EToken.jia->{
                readNextToken(true).let {
                    when(it?.type){
                        // ++KEY
                        EToken.jia -> {
                            pos++
                            nextToken(true, EToken.KEY,"只能是 ++变量").let {
                                VARSELF(next(j2stat),2)
                            }
                        }
                        EToken.INT -> {
                            pos++
                            NumNode(it.text.toInt())
                        }
                        EToken.DOUBLE -> {
                            pos++
                            NumNode(it.text.toDouble())
                        }
                        else ->  null
                    }
                }
            }
            EToken.jian->{
                readNextToken(true).let {
                    when(it?.type){
                        // --KEY
                        EToken.jian -> {
                            pos++
                            nextToken(true, EToken.KEY,"只能是 --变量").let {
                                VARSELF(next(j2stat),3)
                            }
                        }
                        EToken.INT -> {
                            pos++
                            NumNode(-it.text.toInt())
                        }
                        EToken.DOUBLE -> {
                            pos++
                            NumNode(-it.text.toDouble())
                        }
                        else ->  null
                    }
                }
            }

            EToken.KEY -> {
                // gfun   key(xx) {}?
                readNextToken().let {

                    // 最后一个token 那就是变量
                    if (it == null) {
                        VarNode(token)
                    }
                    else {

                        // : KEY ZKH (gfunkey ((','|'，') gfunkey)*)? YKH zdkh (KEY ((','|'，') KEY)* '->' nb* )? stat '}' #gfun1
                        if (it.type == EToken.zkh){
                            pos++
                            gfun(token.text,it,nullJ2)
                        }

                        else{
                            // 两模式   key[取数组]   key.xx 取类 ，不是就变量
                            obj2(VarNode(token),j2stat)
                        }
                    }
                }


            }

            else -> {
                 J2Utils.log("未知类型 obj",token.type,token.text)
                null
            }
        }


        if (one == null) throw TokenException.inputError("匹配失败 此处或多或少或符号不对 【${token.pos}.${token.type}:${token.text}】",token)

        else return one
    }

    /**
     * gfun|   funName()   , 参数中输入 funName，token 应为 ( 左括号。
     * @param 取 gfun之前先取到 KEY 。 默认就先创建 VAR 了。若取
     */
    protected fun gfun(funName : String,token: Token,j2stat: J2Node) : Node {

        // 目前就obj取，obj 中已判定token 类型，这里不再判定
        // if(token.type != zkh) throw

        val gfun = GFunNode(funName,token)

        var isEon = false

        // 取参数
        var nx = nextToken()
        while (nx.type != EToken.ykh){

            // KEY 可能会组成  EONJNode,  key:value
            if (nx.type == EToken.KEY){


                // 读下一个，为冒号就达成 eonj
                readNextToken().let {
                    if (it?.type == EToken.maohao){
                        pos++ // 跳过冒号
                        EONJNode(nx).let {eonj->
                            eonj.value = gfun_par(nextToken())
                            gfun.pars.add(eonj)
                        }
                        isEon = true
                    }else{
                        gfun.pars.add(gfun_par(nx))
                    }
                }

            }else{
                gfun.pars.add(gfun_par(nx))
            }
            nx = nextToken()



            // 闭环后后面还跟着指令
            if (nx.type == EToken.ykh) break

            // 还有下一个参数
            if (nx.type == EToken.douhao) nx = nextToken()



            // 参数指令
            // 参数错误
            else throw TokenException.langError(nx)

        }


        // 后面有概率跟着回调参数
        readNextToken().let {
            if (it?.type == EToken.start && it.text == "{"){
                pos++
                gfun.pars.add(pStat2(it))
            }
        }

        // EONJ合并为 EONNode
        if (isEon){
            EONNode().let { eon->
                gfun.pars.forEach {
                    if (it !is EONJNode) throw TokenException.inputError("参数输入无效 ",token)
                    else eon.put(it)
                }
                gfun.pars.clear()
                gfun.pars.add(eon)
            }

        }

        // 可能存在 gfun.obj2
        return obj2(gfun,j2stat)


    }

    // 取关键词后匹配，三种结果  key.obj   key[suan]   key++， 都匹配不了就属于对象自身
    protected fun obj2(one : Node, j2stat: J2Node) : Node {
        readNextToken().let {
            if (it == null) return one
            when(it.type){
                // key.obj
                EToken.xiaoshudian -> {
                    pos++
                    val x = obj(nextToken(),j2stat)
                    obj2(Obj2Node(one,x),j2stat)
                }

                // key[suan]
                EToken.zzkh -> {
                    pos++
                    suan(nextToken(),j2stat).let { right ->
                        // 需闭合
                        nextToken(true, EToken.yzkh,"此时应当输入闭合符 ] ")
                        obj2(Obj1Node(one,right),j2stat)
                    }
                }

                // key++ ， key--
                EToken.jia,EToken.jian -> {
                    pos++
                    // 后面这个也跟着为同字符，才成立
                    readNextToken().let {nx ->
                        if (it.type == nx?.type){
                            pos++
                            VARSELF(one,if (it.type==EToken.jia) 0 else 1)
                        }else{
                            // 不能组成自增、自减， 就回位让 suan 生成
                            pos--
                            null
                        }
                    }
                }

                else -> null

            }.let {
                return if (it ==null) return one
                else it
            }
        }
    }
    protected fun obj2_next(left : Obj2Node) : Obj2Node{
        readNextToken()?.let {
            if (it.type == EToken.xiaoshudian){

            }
        }
        return left
    }

    protected fun obj3(one : Node, j2stat: J2Node) : Node {
        // obj.obj
        readNextToken(true).let {
            if (it?.type == EToken.xiaoshudian){
                pos++
                val x = obj(nextToken(),j2stat)
                return Obj2Node(one,x)
            }else{
                return one
            }
        }
    }

    /**
     * 运算对象。 在obj 基础上多了算术运算, 包含 obj
     * suan (%+-) suan , suan = obj ，（suan）
     */
    protected fun suan(token: Token,j2stat: J2Node) : Node {
        // a
        suan_obj(token,j2stat).let { a->

            // J2Utils.log("bb",a.toString(),pos,tokens[pos].type,tokens[pos].text)
            // 判断算术条件
            readNextToken().let {a1->

                if (a1 == null) return a
                when(a1.type){
                    EToken.ysf, EToken.jian,EToken.jia -> {
                        pos++
                        return suan_next(SuanNode(a,suan_obj(nextToken(),nullJ2),a1))
                    }
                    else -> return a
                }
            }
        }

    }

    // 优先取括号
    private fun suan_obj(token: Token,j2stat: J2Node) : Node{
        if (token.type == EToken.zkh){
            suan(nextToken(),nullJ2).let { n->
                nextToken(true,EToken.ykh,"此处缺少闭合括号，但你为 ${token.type}(${token.text})")
                return n
            }
        }
        else return obj(token,j2stat)
    }
    private fun suan_next(left : SuanNode) : SuanNode{
        readNextToken().let { token->
            if (token == null) return left
            when(token.type){
                // 乘积 ，若left为加减，就拆解它
                EToken.ysf -> {
                    pos++
                    if (left.ysfTokenType != EToken.ysf){
                        // 重新拆解
                        left.right = SuanNode(left.right!!,suan_obj(nextToken(),nullJ2),token)
                        return suan_next(left)
                    }else{
                        return suan_next(SuanNode(left,suan_obj(nextToken(),nullJ2),token))
                    }
                }

                EToken.jia,EToken.jian -> {
                    pos++
                    return suan_next(SuanNode(left,suan_obj(nextToken(),nullJ2),token))
                }
                else -> return left
            }
        }
    }



    /**
     *  判断，但以获取obj,   ifx2 | suan
     *  v0加减，v1乘除，v2算术判断，v3且或
     */
    protected fun ifx(token: Token,j2stat: J2Node,level : Int = -1) : Node {
        // ! ifx
        // ifx [算术判断 > < ] ifx  （ifx2计算）
        // (ifx)
        // suan

        when(token.type){
            // (ifx)
            EToken.zkh->{
                ifx(nextToken(),j2stat).let {
                    // 闭包
                    nextToken(true, EToken.ykh,"缺少闭包")
                    return ifx2(it,j2stat)
                }
            }

            // !ifx
            EToken.sspd -> {
                if (token.text == "!"){
                    return ifx2(IFX(ifx(nextToken(),j2stat),"!"),j2stat,2)
                }else{
                    throw TokenException.inputError("ifx|此处不应输入 ${token.text}",token)
                }
            }

            else -> {
                return ifx2(suan(token,j2stat),j2stat,level)
            }

        }
    }

    // ifx (>|<|ysf) ifx ,
    protected fun ifx2(left : Node, j2stat: J2Node,level : Int = -1) : Node {
        //  先取到 ifx , 判断是否成立 ifx [算术判断 > < ] ifx ，成立则计算，不成立则返回 left。

        readNextToken()?.let {
            when(it.type){
                // 含运算符，条件成立   a>b
                EToken.ysf, EToken.dayu, EToken.shaoyu, EToken.sspd -> {
                    pos++
                    ifx(nextToken(),j2stat,2).let { right->
                        // 这里 ↓ 的 level 已被 ↑ 消耗 。
                        return ifx2(IFX(left,it.text,right),j2stat,-1) // 等级消耗完，
                    }
                }

                // 类型判断  left is right
                EToken.NIS,EToken.IS -> {
                    pos++
                    nextToken().let { e->
                        ifx(e,j2stat,2).let { right->
                            // 这里 ↓ 的 level 已被 ↑ 消耗 。
                            return ifx2(IFXIS(left,it.type == EToken.IS,right),j2stat,-1)
                        }
                    }

                }

                // ifx || ifx, ifx && ifx . 等级较大
                EToken.huo,EToken.qie -> {
                    // 且或为v3。 需先连接前面组合
                    if (level in 0..2){
                        return left
                    }else{
                        pos++ // 进1
                        nextToken().let { e->
                            ifx(e,j2stat).let { right->
                                return IFXOR(left,it.type == EToken.huo,right)
                            }
                        }
                    }

                }

                else -> {
                    return left
                }
            }
        }
        return left
    }

    /**
     * 值  ifx | E2 | E3 | JS | layout | eon | eonj
     */
    protected fun zhi(j2stat: J2Node) : Node {
        return zhi(nextToken())
    }
    protected fun zhi(token: Token) : Node {

        // 这种值只有EON
        return when(token.type){
            EToken.start -> if (token.text == "{") pEon(token,nullJ2) else ifx(token,nullJ2)
            EToken.zzkh ->  pArr(token)
            EToken.layout ->  LayoutNode(token)
            EToken.j2-> E3Node(token)
            EToken.js-> JsNode(token)
            EToken.ex-> ExNode(token)
            else-> ifx(token,nullJ2)
        }




        // suan
        /*try {
            zhi = ifx(token,nullJ2)
        }
        catch (e : TokenException){
            J2Utils.log("zhi|取ifx 失败")
            // e.printStackTrace()
            pos= startPos
        }*/

        // eon
        /*if (zhi ==null){
            try {
                zhi = pEon(token,nullJ2)
            }catch (e : TokenException){
                J2Utils.log("zhi|取eon失败")
            }
        }*/




    }

    // 数组 [1,2,3,4,5]
    protected fun pArr(startToken: Token) : ARRNode {
        // startToken 需确定为 [  这里就不再次确定了

        val arrNode = ARRNode()


        // 只要不是结束符，就一直找新节点，记录节点
        // a+b
        var nxtoken = nextToken()
        while (nxtoken.type != EToken.yzkh){

            // 得到节点
            arrNode.put(nextLine(nxtoken,nullJ2))

            // 重置J2
            nxtoken = nextToken()

            // 为逗号就下一个
            if (nxtoken.type == EToken.douhao) nxtoken = nextToken()
        }

        return arrNode
    }

    /**
     * 取功能参数  只有 gfun 有用到！
     */
    protected fun gfun_par(token : Token) : Node {
        return when(token.type){

            // 1. 回调参数  {x-> stat} , 2 Eon {a:value,b:value}  ，　３．　纯回调　{stat}
            EToken.start -> {

                // 记录起始匹配位置，匹配失败就重置
                val startPos = pos

                // 尝试匹配EON
                if (token.text == "{"){
                    try {
                        return pEon(token,nullJ2)
                    }
                    catch (e : TokenException){
                        // e.printStackTrace()
                        J2Utils.log("并非EON")

                        // 失败就回正  让下一个读
                        pos = startPos
                    }
                }

                // 不要在 try 中返回内容
                return pStat2(token)
            }

            // (){xx:stat} | (funkey,funkey){xx:stat}
            EToken.zkh -> throw NullException("gfun_par { 小括号")

            // 直接填入值
            else -> zhi(token)
        }
    }

    /**
     * {key:node,key:node}   冒号不可以为 ->
     */
    protected fun pEon(startToken: Token,j2stat: J2Node) : EONNode {
        if (startToken.text != "{") throw TokenException.inputError("组成EON 需为 { 符号",startToken)

        val eonNode = EONNode()
        eonNode.setStartToken(startToken)

        // 取键
        var nxToken : Token = nextToken()
        while (nxToken.type != EToken.end){

            // key
            if (nxToken.type != EToken.KEY) throw TokenException.inputError("EON语法错误",nxToken)
            val kj = EONJNode(nxToken)

            // :
            nextToken().let {
                if (it.type != EToken.maohao && it.text != ":") throw TokenException.inputError("EON语法错误，应 key:value",it)
            }

            // node
            kj.value = next(j2stat)

            // 入
            eonNode.put(kj)


            //  跟着是逗号，继续下一个键，闭包号 } ， EON 读取完毕
            nextToken().let {
                // 逗号 下一个
                if (it.type == EToken.douhao)  nxToken = nextToken()
                else if (it.type == EToken.end && it.text == "}") return eonNode
                else throw TokenException.inputError("EON语法不完整  应 {键名:值,键名:值}",it)
            }
        }
        return eonNode
    }

    /**
     * 取下一个必须为参数集的值，(pars) ， 左右括号也在此函数消耗
     */
    protected fun gPars() : List<ParNode>{

        // 必须必须 首个字符为括号  否则直接报错 ！
        nextToken(true,EToken.zkh,"此处应输入括号叭").let {
            val pars = ArrayList<ParNode>()
            //  KEY : ParType  或 单 PAR
            var nextToken = nextToken()

            // 循环 取到右括号 结束
            while (nextToken.type != EToken.ykh){
                if (nextToken.type == EToken.KEY){
                    val par = ParNode(nextToken)

                    // 顺利取下一个
                    nextToken().let {
                        // 为冒号即为参数
                        if (it.type == EToken.maohao){
                            par.type = parType()

                            // 继续取后面的
                            nextToken = nextToken()
                        }else{
                            nextToken = it
                        }

                    }

                    pars.add(par)


                    // 下一个是逗号时，还有参数
                    if (nextToken.type == EToken.douhao) {
                        nextToken = nextToken()
                    }

                }
                else{
                    throw TokenException.inputError("gpars|应输入参数或闭包 而你输入${nextToken.text}",nextToken)
                }

            }
            return pars
        }

    }

    /**
     * 取下一个必须为参数类型的值 KEY ||  KEY<CLAZZ>
     */
    protected fun parType() : ParTypeNode {
        // class ||   class<a,b,c>

        // KEY，参数名
        val par = ParTypeNode(tokens[pos]) // 这里存入 token 主要为了确定起始位置

        // name
        nextToken(true,EToken.KEY,"参数类型错误").let {
            par.name = it.text
        }


        // 跟着 < 少于号。使用了泛类
        readNextToken()?.let {
            if (it.type == EToken.shaoyu){
                pos++ // 取 <
                var nx = nextToken()
                while (nx.type != EToken.dayu){
                    when(nx.type){
                        // 这个才是对的
                        EToken.KEY -> {
                            par.fans.add(nx.text)

                            // 继续
                            nx = nextToken()

                            // 还有泛类
                            if (nx.type == EToken.douhao){
                                nx = nextToken()
                            }
                        }

                        // 组不成泛类，但写法是正常的
                        else -> throw TokenException.inputError("语法有误，与上下文不匹配",nx)
                    }
                }
            }
        }

        return par

    }

}